/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.muni.fi.pb138.jprvyd;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.exist.xmldb.EXistResource;
import org.xmldb.api.DatabaseManager;
import org.xmldb.api.base.*;
import org.xmldb.api.modules.XPathQueryService;

/**
 *
 * @author tom
 */
public class PaymentMethodManagerImpl implements PaymentMethodManager {

    private static String URI = "xmldb:exist://localhost:8080/exist/xmlrpc/db/jprvyd";
    private Database database = new org.exist.xmldb.DatabaseImpl();
    private Collection col;
    private XPathQueryService xpqs;
    private FileHandler handler;
    public static final Logger l = Logger.getLogger("ContactManagerImplLogger");

    public PaymentMethodManagerImpl() {
        this(URI);
    }

    public PaymentMethodManagerImpl(String URI) {
        try {
            handler = new FileHandler("log/log.log");
            l.addHandler(handler);

            DatabaseManager.registerDatabase(database);
            col = DatabaseManager.getCollection(URI);
            xpqs = (XPathQueryService) col.getService("XPathQueryService", "1.0");
            xpqs.setProperty("indent", "yes");
        } catch (Exception ex) {
            l.log(Level.SEVERE, "Error establishing database connection", ex);
        }
        l.log(Level.INFO, "DB connection established");
    }

    /**
     * creates a payment method
     * @param paymentMethod - payment method to create
     * @throws IllegalEntityException - when argument payment method is invalid
     */
    @Override
    public void createPaymentMethod(PaymentMethod paymentMethod) throws IllegalEntityException {
        l.log(Level.INFO, "PaymentMethodmanagerImpl.createPaymentMethod: creating payment method");

        if (paymentMethod == null) {
            String info = "PaymentMethodManagerImpl.createPaymentMethod: paymentMethod is null";
            l.log(Level.SEVERE, info);
            throw new IllegalArgumentException(info);
        }

        if (paymentMethod.getId() != null) {
            String info = "PaymentMethodManagerImpl.createPaymentMethod: paymentMethod id is set";
            l.log(Level.SEVERE, info);
            throw new IllegalEntityException(info);
        }

        if (paymentMethod.getName() == null) {
            String info = "PaymentMethodImpl.createPaymentMethod: name is null";
            l.log(Level.SEVERE, info);
            throw new IllegalEntityException(info);
        }

        try {
            Long id;
            if (this.getAllPaymentMethods().isEmpty()) {
                id = Long.parseLong("1");
            } else {
                id = Long.parseLong(xpqs.query("max(for $x in doc(\"paymentmethods.xml\")//method return $x/@id)").getIterator().nextResource().getContent().toString());
                id++;
            }

            String node = "<method id=\"" + id + "\"><name>" + paymentMethod.getName() + "</name></method>";

            String query = "update insert " + node + " into doc(\"paymentmethods.xml\")//paymentmethods";

            ResourceSet res = xpqs.query(query);

            paymentMethod.setId(id);

        } catch (Exception ex) {
            l.log(Level.SEVERE, "PaymentMethodImpl.createPaymentMethod: Exception creating payment method: ", ex);
        }

        l.log(Level.INFO, "PaymentMethodImpl.createPaymentMethod: Payment Method created");

    }

    /**
     * updates payment method
     * @param paymentMethod - payment method to update
     * @throws IllegalEntityException when argument paymentMethod is invalid
     */
    @Override
    public void updatePaymentMethod(PaymentMethod paymentMethod) throws IllegalEntityException {
        l.log(Level.INFO, "PaymentMethodImpl.updatePaymentMethod:Updating paymentMethod");

        if (paymentMethod == null) {
            String info = "ContactManagerImpl.updateContact: contact is null";
            l.log(Level.SEVERE, info);
            throw new IllegalArgumentException(info);
        }

        if (paymentMethod.getName() == null) {
            String info = "PaymentMethodManagerImpl.updatePaymentMethod: name is null";
            l.log(Level.SEVERE, info);
            throw new IllegalEntityException(info);
        }

        if (paymentMethod.getId() == null) {
            String info = "PaymentMethodImpl.updatePaymentMethod: id is null";
            l.log(Level.SEVERE, info);
            throw new IllegalEntityException(info);
        }

        try {
            Long id = paymentMethod.getId();

            String node = "<method id=\"" + id + "\"><name>" + paymentMethod.getName() + "</name></method>";

            if (xpqs.query("for $x in doc(\"paymentmethods.xml\")//method[@id=" + id + "] return $x/name").getIterator().hasMoreResources() == false) {
                throw new IllegalEntityException("PaymentMethodManagerImpl.updatePaymentMethod: payment method does not exist");
            }

            String query = "update replace doc(\"paymentmethods.xml\")//method[@id=" + id + "] with " + node;
            xpqs.query(query);
        } catch (XMLDBException ex) {
            l.log(Level.SEVERE, "PaymentMethodManagerImpl.updatePaymentMethod: Exception updating payment method: ", ex);
        }
        l.log(Level.INFO, "PaymentMethodManagerImpl.updatePaymentMethod: payment method updated");

    }
/**
 * removes payment method
 * @param id of method to remove
 */
    @Override
    public void removePaymentMethod(Long id) {
        l.log(Level.INFO, "PaymentMethodManagerImpl.removePaymentMethod: Removing payment method");

        if (id == null) {
            String info = "PaymentMethodManagerImpl.removePaymentMethod: id is null";
            l.log(Level.SEVERE, info);
            throw new IllegalArgumentException(info);
        }

        try {
            String query = "for $method in doc(\"paymentmethods.xml\")//method[@id=" + id + "] return update delete $method";
            xpqs.query(query);

        } catch (Exception ex) {
            l.log(Level.SEVERE, "PaymentMethodManagerImpl.removePaymentMethod: Exception removing payment method: ", ex);
        }

        l.log(Level.INFO, "PaymentMethodManagerImpl.removePaymentMethod: payment method removed");
    }
/**
 * returns payment method by id
 * @param id - if of method to retrieve
 * @return  payment method with specified id
 */
    @Override
    public PaymentMethod getPaymentMethod(Long id) {
        l.log(Level.INFO, "PaymentMethodManagerImpl.getPaymentMethod: Retrieving payment method");
        PaymentMethod method = new PaymentMethod();

        if (id == null) {
            String info = "PaymentMethodManagerImpl.getPaymentMethod: id is null";
            l.log(Level.SEVERE, info);
            throw new IllegalArgumentException(info);
        }

        try {
            String queryStart = "for $x in doc(\"paymentmethods.xml\")//method where @id=\"" + id + "\" return data($x/";

            ResourceSet resultName = xpqs.query(queryStart + "name)");
            ResourceSet resultId = xpqs.query(queryStart + "@id)");

            ResourceIterator iterName = resultName.getIterator();
            ResourceIterator iterId = resultId.getIterator();

            Resource resName = null;
            Resource resId = null;

            try {
                resName = iterName.nextResource();
                resId = iterId.nextResource();

                method.setName(resName.getContent().toString());
                method.setId(Long.parseLong(resId.getContent().toString()));
            } catch (Exception ex) {
                l.log(Level.SEVERE, "PaymentMethodManagerImpl.getPaymentMethod: Exception getting payment method: ", ex);
                throw new Exception(ex);
            } finally {
                try {
                    ((EXistResource) resName).freeResources();
                    ((EXistResource) resId).freeResources();
                } catch (Exception ex) {
                    l.log(Level.SEVERE, "PaymentMethodManagerImpl.getPaymentMethod: Exception freeing up resources: ", ex);
                }
            }
        } catch (Exception ex) {
            l.log(Level.SEVERE, "PaymentMethodManagerImpl.getPaymentMethod: Excepion occured: ", ex);
        } finally {

            if (col != null) {
                try {
                    col.close();
                } catch (XMLDBException ex) {
                    l.log(Level.SEVERE, "PaymentMethodManagerImpl.getPaymentMethod: Exception closing collection: ", ex);
                }
            }
        }
        l.log(Level.INFO, "PaymentMethodManagerImpl.getPaymentMethod: Contact retrieved");
        if (method.getId() == null) {
            l.log(Level.INFO, "PaymentMethodManagerImpl.getPaymentMethod: No contact found");
            return null;
        } else {
            l.log(Level.INFO, "PaymentMethodManagerImpl.getPaymentMethod: Contact retrieved");
            return method;
        }
    }
    
    /**
     * returns all payment methods
     * @return list of all payment methods
     */
    @Override
    public List<PaymentMethod> getAllPaymentMethods() {
        l.log(Level.INFO, "PaymentMethodManagerImpl.getAllPaymentMethods: Retrieving payment methods");

        List<PaymentMethod> paymentMethods = new ArrayList<PaymentMethod>();

        try {

            String queryStart = "for $x in doc(\"paymentmethods.xml\")//method return data($x/";

            ResourceSet resultName = xpqs.query(queryStart + "name)");
            ResourceSet resultId = xpqs.query(queryStart + "@id)");

            ResourceIterator iterName = resultName.getIterator();
            ResourceIterator iterId = resultId.getIterator();

            Resource resName = null;
            Resource resId = null;

            while (iterName.hasMoreResources()) {

                try {
                    PaymentMethod method = new PaymentMethod();
                    resName = iterName.nextResource();
                    resId = iterId.nextResource();

                    method.setId(Long.parseLong(resId.getContent().toString()));
                    method.setName(resName.getContent().toString());

                    paymentMethods.add(method);

                } finally {

                    try {
                        ((EXistResource) resName).freeResources();
                        ((EXistResource) resId).freeResources();
                    } catch (Exception ex) {
                        l.log(Level.SEVERE, "PaymentMethodManagerImpl.getAllPaymentMethods: Exception occured: ", ex);

                    }
                }
            }
        } catch (Exception ex) {
            l.log(Level.SEVERE, "PaymentMethodManagerImpl.getAllPaymentMethods: Exception occured: ", ex);
        } finally {

            if (col != null) {
                try {
                    col.close();
                } catch (XMLDBException ex) {
                    l.log(Level.SEVERE, "PaymentMethodManagerImpl.getAllPaymentMethods: Exception occured: ", ex);
                }
            }
        }
        l.log(Level.INFO, "PaymentMethodManagerImpl.getAllPaymentMethods: Payment methods retrieved");
        return paymentMethods;
    }
}